﻿using System;
using System.IO;
using System.Text;
using System.Windows;
using System.Windows.Documents;
using Notoric.Commanding.CommandBase;

namespace Notoric.ViewModel.Elements
{
    /// <summary>
    /// Adapts a <see cref="Model.Elements.TextSectionElement"/> instance for a Windows Presentation Foundation view.
    /// </summary>
    public class TextSection : Section
    {
        #region Constructors

        /// <summary>
        /// Initializes a new instance of the <see cref="TextSection"/> class.
        /// </summary>
        /// <param name="adaptedElement">
        /// <para>
        /// Type: <see cref="Model.Elements.TextSectionElement"/>
        /// </para>
        /// <para>
        /// The adapted element.
        /// </para>
        /// </param>
        /// <param name="owner">
        /// <para>
        /// Type: <see cref="Page"/>
        /// </para>
        /// <para>
        /// The page that directly owns this section.
        /// </para>
        /// </param>
        /// <exception cref="ArgumentNullException">
        /// Any of the arguments is <see langword="null"/>.
        /// </exception>
        public TextSection(Model.Elements.TextSectionElement adaptedElement, Page owner)
            : base(adaptedElement, owner)
        {
            
        }

        #endregion

        #region ViewModelElement Members

        /// <inheritdoc/>
        public override string ElementNameKey
        {
            get { return elementNameKey; }
        }

        #endregion

        #region Private Fields

        /// <summary>
        /// Holds the dictionary key of a user-friendly name of this element.
        /// </summary>
        private const string elementNameKey = "ViewModel.ElementNames.TextSection";

        #endregion
    }

    /// <summary>
    /// Adapts a <see cref="Model.Elements.RtfTextSectionInteriorElement"/> instance for a Windows Presentation Foundation view.
    /// </summary>
    public class RtfTextSectionInterior : SectionInterior
    {
        #region Constructors

        /// <summary>
        /// Initializes a new instance of the <see cref="RtfTextSectionInterior"/> class.
        /// </summary>
        /// <param name="adaptedElement">
        /// <para>
        /// Type: <see cref="Model.Elements.RtfTextSectionInteriorElement"/>
        /// </para>
        /// <para>
        /// The adapted element.
        /// </para>
        /// </param>
        /// <param name="owner">
        /// <para>
        /// Type: <see cref="TextSection"/>
        /// </para>
        /// <para>
        /// The owner.
        /// </para>
        /// </param>
        /// <exception cref="ArgumentNullException">
        /// Any of the arguments is <see langword="null"/>.
        /// </exception>
        public RtfTextSectionInterior(Model.Elements.RtfTextSectionInteriorElement adaptedElement, TextSection owner)
            : base(adaptedElement, owner, owner.UndoStack, owner.RedoStack)
        {
        }

        #endregion

        #region Event Handlers

        /// <inheritdoc/>
        protected override bool AdaptedElement_PropertyChanged(System.ComponentModel.PropertyChangedEventArgs e)
        {
            if (e.PropertyName == "RtfContent")
                RaisePropertyChanged("Document");
            return base.AdaptedElement_PropertyChanged(e);
        }

        #endregion

        #region Public Methods

        /// <summary>
        /// Creates an undoable command for writing the document content into the model.
        /// </summary>
        /// <param name="content">
        /// <para>
        /// Type: <see cref="FlowDocument"/>
        /// </para>
        /// <para>
        /// The content.
        /// </para>
        /// </param>
        /// <returns>
        /// <para>
        /// Type: <see cref="UndoableCommandBase"/>
        /// </para>
        /// <para>
        /// The created command ready for execution.
        /// </para>
        /// </returns>
        /// <exception cref="ArgumentNullException">
        /// The <paramref name="content"/> is <see langword="null"/>.
        /// </exception>
        public UndoableCommandBase CreateSetContentCommand(FlowDocument content)
        {
            if (content == null)
                throw new ArgumentNullException("content");

            var stream = new MemoryStream();
            string contentString = null;
            
            using (stream)
            {
                var range = new TextRange(content.ContentStart, content.ContentEnd);
                range.Save(stream, DataFormats.Rtf);
                stream.Flush();
                stream.Position = 0;
                var reader = new StreamReader(stream);

                contentString = reader.ReadToEnd();
            }

            return new SetterCommand<string>(this, "RtfContent", contentString, UndoStack, RedoStack);
        }
        
        #endregion

        #region Public Events

        /// <summary>
        /// Occurs when the text selection should be cleared.
        /// </summary>
        public event EventHandler NeedsToClearSelection;

        #endregion

        #region Public Properties

        /// <summary>
        /// Gets the flow document that holds the content of the section.
        /// </summary>
        /// <value>
        /// <para>
        /// Type: <see cref="FlowDocument"/>
        /// </para>
        /// <para>
        /// The flow document that holds the content of the section.
        /// </para>
        /// </value>
        public FlowDocument Document
        {
            get 
            {
                var document = new FlowDocument();
                MemoryStream stream = new MemoryStream(ASCIIEncoding.Default.GetBytes(((Model.Elements.RtfTextSectionInteriorElement)AdaptedElement).RtfContent));
                using (stream)
                {
                    var range = new TextRange(document.ContentStart, document.ContentEnd);
                    range.Load(stream, DataFormats.Rtf);
                }

                return document;
            }
        }

        /// <summary>
        /// Gets or sets the current selection.
        /// </summary>
        /// <value>
        /// <para>
        /// Type: <see cref="TextSelection"/>
        /// </para>
        /// Currently selected <see cref="Document"/> content.
        /// </value>
        public TextSelection Selection
        {
            get { return selection; }
            set
            {
                selection = value;
                IsContentSelected = selection != null && !selection.IsEmpty;

                RaisePropertyChanged("Selection");
            }
        }

        #endregion

        #region ViewModelElement Members

        /// <inheritdoc/>
        public override string ElementNameKey
        {
            get { return FindOwner<TextSection>().ElementNameKey; }
        }

        #endregion

        #region SectionInterior Members

        /// <inheritdoc/>
        protected override void ClearSelectionCore()
        {
            if (NeedsToClearSelection != null)
                NeedsToClearSelection(this, EventArgs.Empty);
        }

        #endregion

        #region Private Fields

        /// <summary>
        /// Holds the value of the <see cref="Selection"/> property.
        /// </summary>
        private TextSelection selection;

        #endregion
    }
}
